/*
 * Task.cpp
 *
 *  Created on: Oct 18, 2012
 *      Author: david
 */

#include "Task.h"
#include "mpi.h"

#include <time.h>

time_t secondsStart = 0;

Task::Task(int numberOfNodes) {
	depth = EMPTY;
	numOfNodes = numberOfNodes;
	maxJob = NULL;
	actualState = NULL;
	isSolved = false;
	lastExpandMovementIndex = EMPTY;
	MPI_Comm_rank(MPI_COMM_WORLD, &my_rank2);
}

Task::~Task() {
	delete maxJob;
	delete actualState;
}

void Task::nexByInreaseDepth() {
	if (logTaskStartNodeInfo && depth == 0) {
		time_t seconds = time(NULL);
		cout << my_rank2 << " starting with node: " << actualState[depth] << endl;
		if (secondsStart != 0) cout << my_rank2 << " starting with node: " << actualState[depth] << " task trval " << secondsStart << " " << seconds << endl;
		if (secondsStart == 0) cout << my_rank2 << " starting with node: " << actualState[depth] << endl;
		secondsStart = seconds;
	}
	if (depth < numOfNodes) {
		if (logTask) logPrintMessage("I'll increase depth");
		depth++;
		actualState[depth] = actualState[depth - 1];
		if (logTask) logPrintTask();
		nextByWidth();
	} else {
		if (logTask) logPrintMessage("cannot increase depth");
		lastExpandMovementIndex = EMPTY;
	}
}

int Task::getLastExpandMovementIndex() {
	return lastExpandMovementIndex;
}

void Task::decreaseDepth() {
	if (logTask) logPrintMessage("I'll decrease depth");
	if (depth >= 0) {
		actualState[depth] = EMPTY;
		depth--;
	}
	if (logTask) logPrintTask();
}

int Task::getDepth() {
	return depth;
}

int16_t* Task::getActualState() {
	return actualState;
}

void Task::nextByWidth() {
	if (actualState[depth] < maxJob[depth]) {
		if (logTask) logPrintMessage("I'll go to width");
		actualState[depth] = actualState[depth] + 1;
		if (logTask) logPrintTask();
		lastExpandMovementIndex = actualState[depth];
	} else {
		if (logTask) logPrintMessage("cannot go to width");
		lastExpandMovementIndex = EMPTY;
	}
}

void Task::init() {
	isSolved = false;
	int i = 0;
	while (actualState[i] != EMPTY) {
		i++;
	}
	depth = i - 1;
	lastExpandMovementIndex = actualState[depth];
	if (logTask) {
		logPrintMessage("task initialized");
		logPrintTask();
	}
}

bool Task::haveWork() {
	return isSolved ? false : (depth == -1 ? false : true);
}

void Task::createFullTask() {
	actualState = new int16_t[numOfNodes];
	maxJob = new int16_t[numOfNodes];

	for (int i = 0; i < numOfNodes; i++) {
		maxJob[i] = numOfNodes - 1;
		actualState[i] = EMPTY;
	}
	if (numOfNodes <= 0) {
		throw new exception();
	}
	actualState[0] = 0;
	init();
}

void Task::receiveNewOne(MPI_Status* p_status) {
	if (actualState != NULL) {
		delete actualState;
	}
	if (maxJob != NULL) {
		delete maxJob;
	}
	actualState = new int16_t[numOfNodes];
	maxJob = new int16_t[numOfNodes];
	int16_t* buffer = new int16_t[2 * numOfNodes];
	int bufferSize = 2 * numOfNodes * sizeof(int16_t);
	int position = 0;
	if (logReceiveTask) {
		int from = p_status->MPI_SOURCE;
		cout << my_rank2 << " start receiving task from " << from << endl;
	}
	MPI_Recv(buffer, bufferSize, MPI_PACKED, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, p_status);

	MPI_Unpack(buffer, bufferSize, &position, actualState, numOfNodes, MPI_INT16_T, MPI_COMM_WORLD);
	MPI_Unpack(buffer, bufferSize, &position, maxJob, numOfNodes, MPI_INT16_T, MPI_COMM_WORLD);
	if (logReceiveTask) {
		int from = p_status->MPI_SOURCE;
		cout << my_rank2 << " end receiving task from " << from << endl;
	}
	if (logReceiveTaskData) {
		logPrintTask();
	}
	init();
}

void Task::logBuffer(int16_t* buffer) {
	for (int var = 0; var < 2 * numOfNodes; ++var) {
		printf(" %4d", buffer[var]);
		if (var == numOfNodes - 1) {
			cout << endl;
		}
	}
	cout << endl;
}

bool Task::haveEnoughToSplit() {
	if (!haveWork()) {
		return false;
	}
	int posibleDepthExpand = (numOfNodes - depth);
	int posibleWidthExpand = (numOfNodes - actualState[depth]);
	int maxPossibleExpand = (posibleDepthExpand < posibleWidthExpand) ? posibleWidthExpand : posibleDepthExpand;
	if (maxPossibleExpand > taskTreshold) {
		return true;
	}
	return false;
}

void Task::splitAndSend(int destination, int* p_isTokenDirty) {
	if (haveEnoughToSplit()) {

		if (destination < my_rank2) {
			*p_isTokenDirty = true;
		}

		int16_t* buffer = new int16_t[2 * numOfNodes];
		int bufferSize = 2 * numOfNodes * sizeof(int16_t);
		int position = 0;

		int16_t* actualStateNew = new int16_t[numOfNodes];
		int16_t* maxJobNew = new int16_t[numOfNodes];

		int changeLevel = 0;
		// searching change level + copy first part of task
		for (int i = 0; i < numOfNodes; ++i) {
			if (actualState[i] == maxJob[i]) {
				actualStateNew[i] = actualState[i];
				maxJobNew[i] = maxJob[i];
			} else {
				changeLevel = i;
				break;
			}
		}
		int remain = maxJob[changeLevel] - actualState[changeLevel];
		maxJobNew[changeLevel] = maxJob[changeLevel];
		maxJob[changeLevel] = actualState[changeLevel] + remain / 2;
		actualStateNew[changeLevel] = maxJob[changeLevel] + 1;
		if (logSplitPrep) logSplitTask(changeLevel);

		//copy second part of task
		for (int i = changeLevel + 1; i < numOfNodes; i++) {
			maxJobNew[i] = numOfNodes - 1;
			actualStateNew[i] = EMPTY;
		}

		if (logSendTask) cout << my_rank2 << " start sending task to " << destination << " buffer size:" << bufferSize << endl;

		MPI_Pack(actualStateNew, numOfNodes, MPI_INT16_T, buffer, bufferSize, &position, MPI_COMM_WORLD);
		MPI_Pack(maxJobNew, numOfNodes, MPI_INT16_T, buffer, bufferSize, &position, MPI_COMM_WORLD);
		MPI_Send(buffer, position, MPI_PACKED, destination, MSG_WORK_SENT, MPI_COMM_WORLD);

		delete maxJobNew;
		delete actualStateNew;

		if (logSendTask) cout << my_rank2 << " end sending task to " << destination << " buffer size:" << bufferSize << endl;
		if (logSplit) {
			logBuffer(buffer);
			logPrintTask();
			sleep(1);
		}
	} else {
		int i = 1;
		if (logSendNoWork) cout << my_rank2 << " start sending work - no work to " << 0 << endl;
		MPI_Send(&i, 1, MPI_INT, destination, MSG_WORK_NOWORK, MPI_COMM_WORLD);
		if (logSendNoWork) cout << my_rank2 << " end sending work - no work to " << 0 << endl;
	}

}

void Task::setTaskSolved() {
	isSolved = true;
}

void Task::logPrintTask() {
	cout << my_rank2 << " Task state. depth is: " << depth << endl;
	for (int i = 0; i < numOfNodes; i++) {
		printf(" %5d , %5d\n", actualState[i], maxJob[i]);
	}
}

void Task::logSplitTask(int changeLevel) {
	cout << "deleniiii " << "  maxJob[changeLevel] - actualState[changeLevel]  " << maxJob[changeLevel] << "  " << actualState[changeLevel]
			<< "  maxJob[changeLevel] == actualState[changeLevel] " << (maxJob[changeLevel] == actualState[changeLevel]) << "  5 == 4 " << (5 == 4)
			<< "  actualStateNew[changeLevel] = maxJob[changeLevel] + 1  " << maxJob[changeLevel] + 1 << "  actualStateNew[changeLevel] = maxJob[changeLevel] + 1;  "
			<< maxJob[changeLevel] + 1 << endl;
}

